package com.common.util;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class DESedeUtil {
	// 算法DESede
	private static final String Algorithm = "DESede";

	// 工作模式CBC(ECB)，填充模式PKCS5Padding(NoPadding)
	// eg: DESede/CBC/PKCS5Padding, DESede/ECB/PKCS5Padding
	private static final String Transformation = "DESede/CBC/PKCS5Padding";

	// 十六进制数字
	private final static String HEX_DIGITAL = "0123456789ABCDEF";

	
	/**
	 * 
	 * @author leixin
	 * @createTime 下午10:08:04
	 * @param encryptKey  秘钥字符串
	 * @param src   明文字符串
	 * @param ivSpec   向量必须是8位，取向量的规则是去id的前八位
	 * @return
	 */
	public static String encryptMode(String encryptKey, String src, String ivSpec) {
		try {
			byte[] keyByte = DESedeUtil.getKeyByte(encryptKey);
			// 根据给定的字节数组和算法构造一个密钥
			SecretKey deskey = new SecretKeySpec(keyByte, Algorithm);
			// 向量iv,ECB不需要向量iv，CBC需要向量iv
			// CBC工作模式下，同样的密钥，同样的明文，使用不同的向量iv加密 会生成不同的密文，用主键的前8位作为向量
			IvParameterSpec iv = new IvParameterSpec(ivSpec.getBytes());
			// 实例化负责加密/解密的Cipher工具类
			Cipher c1 = Cipher.getInstance(Transformation);
			// 初始化为加密模式
			c1.init(Cipher.ENCRYPT_MODE, deskey, iv);
			return bytesToHexString(c1.doFinal(src.getBytes()));
		} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 根据密钥加密
	 * @param keyByte 密钥
	 * @param src 明文
	 * @param ivSpec CBC工作模式下，同样的密钥，同样的明文，使用不同的向量iv加密 会生成不同的密文
	 * @return 密文
	 */
	public static String encryptMode(byte[] keyByte, String src, String ivSpec) {
		try {
			// 根据给定的字节数组和算法构造一个密钥
			SecretKey deskey = new SecretKeySpec(keyByte, Algorithm);
			// 向量iv,ECB不需要向量iv，CBC需要向量iv
			// CBC工作模式下，同样的密钥，同样的明文，使用不同的向量iv加密 会生成不同的密文，用主键的前8位作为向量
			IvParameterSpec iv = new IvParameterSpec(ivSpec.getBytes());
			// 实例化负责加密/解密的Cipher工具类
			Cipher c1 = Cipher.getInstance(Transformation);
			// 初始化为加密模式
			c1.init(Cipher.ENCRYPT_MODE, deskey, iv);
			return bytesToHexString(c1.doFinal(src.getBytes()));
		} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	/**
	 * 加密数据必须是24位，不足补0；超出24位则只取前面的24数据
	 * @param key 密钥
	 * @return 24位密钥
	 * @throws UnsupportedEncodingException
	 */
	public static byte[] getKeyByte(String key) throws UnsupportedEncodingException {
		byte[] data = key.getBytes();
		int len = data.length;
		byte[] newdata = new byte[24];
		System.arraycopy(data, 0, newdata, 0, len > 24 ? 24 : len);
		return newdata;
	}

	/**
	 * 根据密钥解密
	 * @param keyByte  密钥
	 * @param src 明文
	 * @param ivSpec CBC工作模式下，同样的密钥，同样的明文，使用不同的向量iv加密 会生成不同的密文
	 * @return 解密失败返回null
	 */
	public static String decryptMode(byte[] keyByte, byte[] src, String ivSpec) {
		try {
			// 生成密钥
			SecretKey deskey = new SecretKeySpec(keyByte, Algorithm);
			// 解密
			IvParameterSpec iv = new IvParameterSpec(ivSpec.getBytes());
			Cipher c1 = Cipher.getInstance(Transformation);
			c1.init(Cipher.DECRYPT_MODE, deskey, iv);
			byte[] data = c1.doFinal(src);
			return new String(data);
		} catch (java.security.NoSuchAlgorithmException e1) {
			e1.printStackTrace();
		} catch (javax.crypto.NoSuchPaddingException e2) {
			e2.printStackTrace();
		} catch (java.lang.Exception e3) {
			e3.printStackTrace();
		}
		return null;
	}
	
	
	
	/**
	 * 
	 * @author leixin
	 * @createTime 下午10:11:20
	 * @param decryptKey  秘钥
	 * @param src  密文字符串
	 * @param ivSpec  向量必须是8位，取向量的规则是去id的前八位
	 * @return
	 */
	public static String decryptMode(String decryptKey, String src, String ivSpec) {
		try {
			byte[] hexStringToByte = DESedeUtil.hexStringToByte(src);
			byte[] keyByte = DESedeUtil.getKeyByte(decryptKey);
			// 生成密钥
			SecretKey deskey = new SecretKeySpec(keyByte, Algorithm);
			// 解密
			IvParameterSpec iv = new IvParameterSpec(ivSpec.getBytes());
			Cipher c1 = Cipher.getInstance(Transformation);
			c1.init(Cipher.DECRYPT_MODE, deskey, iv);
			byte[] data = c1.doFinal(hexStringToByte);
			return new String(data);
		} catch (java.security.NoSuchAlgorithmException e1) {
			e1.printStackTrace();
		} catch (javax.crypto.NoSuchPaddingException e2) {
			e2.printStackTrace();
		} catch (java.lang.Exception e3) {
			e3.printStackTrace();
		}
		return null;
	}


	/**
	 * 
	 * @author leixin
	 * @createTime 下午10:13:12
	 * @param src 明文 
	 * @param encryptKey key
	 * @return 密文
	 * @throws Exception
	 */
	public static String encryptDES(String src, String encryptKey){
		byte[] encryptedData;
		String result = null;
		try {
			//加密前的字符串长度必须是8的倍数，因此给不足长度的部分在头部分补0
			String encryptSrc = addZeroToString(src);
			SecretKeySpec key = new SecretKeySpec(getKey(encryptKey), "DES");
			Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");
			cipher.init(Cipher.ENCRYPT_MODE, key);
			encryptedData = cipher.doFinal(encryptSrc.getBytes());
			result = bytesToHexString(encryptedData);
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return result;
	}

	
	

	/***
	 * 解密数据
	 * 
	 * @param decryptString 密文
	 * @param decryptKey
	 * @return
	 * @throws Exception
	 */
	public static String decryptDES(String decryptString, String decryptKey){
		String result = null;
		try {
			SecretKeySpec key = new SecretKeySpec(getKey(decryptKey), "DES");
			Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");
			cipher.init(Cipher.DECRYPT_MODE, key);
			byte decryptedData[] = cipher.doFinal(hexStringToByte(decryptString));
			result = new String(decryptedData);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if (null != result) {
			return replaceStringZero(result);
		}
		return null;
	}

	
	/**
	 * 把16进制字符串转换成字节数组
	 * @param hex 16进制字符串
	 * @return 二进制字节数组
	 */
	public static byte[] hexStringToByte(String hex) {
		int len = (hex.length() / 2);
		byte[] result = new byte[len];
		char[] achar = hex.toCharArray();
		for (int i = 0; i < len; i++) {
			int pos = i * 2;
			result[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));
		}
		return result;
	}

	private static byte toByte(char c) {
		byte b = (byte) HEX_DIGITAL.indexOf(c);
		return b;
	}


	/**
	 * 把字节数组转换成16进制字符串
	 * @param bArray   二进制字节数组
	 * @return 16进制字符串
	 */
	public static final String bytesToHexString(byte[] bArray) {
		StringBuffer sb = new StringBuffer(bArray.length);
		String sTemp;
		for (int i = 0; i < bArray.length; i++) {
			sTemp = Integer.toHexString(0xFF & bArray[i]);
			if (sTemp.length() < 2) {
				sb.append(0);
			}
			sb.append(sTemp);
		}
		return sb.toString().toUpperCase();
	}

	
	
	/**
	 * 自定义一个key
	 * @param keyRule
	 */
	public static byte[] getKey(String keyRule) {
		Key key = null;
		byte[] keyByte = keyRule.getBytes();
		// 创建一个空的八位数组,默认情况下为0
		byte[] byteTemp = new byte[8];
		// 将用户指定的规则转换成八位数组
		for (int i = 0; i < byteTemp.length && i < keyByte.length; i++) {
			byteTemp[i] = keyByte[i];
		}
		key = new SecretKeySpec(byteTemp, "DES");
		return key.getEncoded();
	}
	
	/**
	 * 給字符串加0
	 * @author leixin
	 * @createTime 下午10:29:38
	 * @param src
	 * @return
	 */
	public static String addZeroToString(String src) {
		String pattern="000000000000000000000000";
		String result = pattern.substring(0, pattern.length()-src.length())+src;
		return result;
	}
	
	
	
	/**
	 * 去掉字符串前面的零
	 * @author leixin
	 * @createTime 下午10:27:31
	 * @param src
	 * @return
	 */
	public static String replaceStringZero(String src) {
		String result = src.replaceAll("^(0+)", "");
		return result;
		
	}
	
}
